// MyApplication.cpp : Defines the entry point for the console application.
//


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <map>
#include <list>
#include <curl/curl.h>

#include <openssl/bio.h>
#include <openssl/buffer.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>


using namespace std;

typedef struct curl_slist* CURLHEADER;
typedef std::list< pair<string,string> > KEYVALUELIST;
CURLHEADER headers = NULL;
CURL *curl;
CURLcode res;
static const EVP_MD* evp_md = EVP_sha256();

/**
 * determine the value of a base64 encoding character
 *
 * @param base64char the character of which the value is searched
 * @return the value in case of success (0-63), -1 on failure
 */  
int _base64_char_value(char base64char)
 {
    if (base64char >= 'A' && base64char <= 'Z')
 return base64char-'A';
    if (base64char >= 'a' && base64char <= 'z')
 return base64char-'a'+26;
    if (base64char >= '0' && base64char <= '9')
 return base64char-'0'+2*26;
    if (base64char == '+')
 return 2*26+10;
    if (base64char == '/')
 return 2*26+11;
    return -1;
}

/**
 * decode a 4 char base64 encoded byte triple
 *
 * @param quadruple the 4 characters that should be decoded
 * @param result the decoded data
 * @return lenth of the result (1, 2 or 3), 0 on failure
 */  
int _base64_decode_triple(char* quadruple, unsigned char *result)
 {
    int i, triple_value, bytes_to_decode = 3, only_equals_yet = 1;
    int char_value[4];

    for (i=0; i<4; i++)
 char_value[i] = _base64_char_value(quadruple[i]);

    /* check if the characters are valid */
    for (i=3; i>=0; i--)
    {
 if (char_value[i]<0)
 {
     if (only_equals_yet && quadruple[i]=='=')
     {
  /* we will ignore this character anyway, make it something
   * that does not break our calculations */
  char_value[i]=0;
  bytes_to_decode--;
  continue;
     }
     return 0;
 }
 /* after we got a real character, no other '=' are allowed anymore */
 only_equals_yet = 0;
    }

    /* if we got "====" as input, bytes_to_decode is -1 */
   if (bytes_to_decode < 0)
 bytes_to_decode = 0;

    /* make one big value out of the partial values */
    triple_value = char_value[0];
    triple_value *= 64;
    triple_value += char_value[1];
    triple_value *= 64;
    triple_value += char_value[2];
    triple_value *= 64;
    triple_value += char_value[3];

    /* break the big value into bytes */
    for (i=bytes_to_decode; i<3; i++)
 triple_value /= 256;
    for (i=bytes_to_decode-1; i>=0; i--)
    {
 result[i] = triple_value%256;
 triple_value /= 256;
    }

    return bytes_to_decode;
} 

/**
 * decode base64 encoded data
 *
 * @param source the encoded data (zero terminated)
 * @param target pointer to the target buffer
 * @param targetlen length of the target buffer
 * @return length of converted data on success, -1 otherwise
 */  
size_t base64_decode(char *source, unsigned char *target, size_t targetlen)
 {
    char *src, *tmpptr;
    char quadruple[4], tmpresult[3];
    int i, tmplen = 3;
    size_t converted = 0;

    
    src = (char *)malloc(strlen(source)+5);
    if (src == NULL)
 return -1;
    strcpy(src, source);
    strcat(src, "====");
    tmpptr = src;

    
    while (tmplen == 3)
    {
 
 for (i=0; i<4; i++)
 {
    
     while (*tmpptr != '=' && _base64_char_value(*tmpptr)<0)
  tmpptr++;

     quadruple[i] = *(tmpptr++);
 }

 
 tmplen = _base64_decode_triple((char*)quadruple, (unsigned char*)tmpresult);

 
 if (targetlen < tmplen) {
     free(src);
     return -1;
 }

 
 memcpy(target, tmpresult, tmplen);
 target += tmplen;
 targetlen -= tmplen;
 converted += tmplen;
    }

    free(src);
    return converted;
} 



std::ostream& operator << (std::ostream& s,const CURLHEADER& h)
{
	CURLHEADER temp = h;
	while(temp != NULL){
		s << temp -> data << "------";
		temp = temp -> next;
	}
	return s;
}

CURLHEADER buildHeaders(const KEYVALUELIST& m)
{
	for(KEYVALUELIST::const_iterator it = m.begin();it != m.end();it++){
		string tempheader = it->first;		
		tempheader = tempheader.append(":");
		tempheader = tempheader.append(" ");
		tempheader = tempheader.append(it ->second);//key: value
		headers = curl_slist_append(headers,tempheader.c_str());
	}
	return headers;
}

string get_date() 
{
	//return "Wed, 18 Nov 2009 19:36:22 GMT";
	char buf[100];
	time_t t = time(NULL);
	strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", gmtime(&t));
	return buf;
}

char *base64(const char *input, int length)
{
  BIO *bmem, *b64;
  BUF_MEM *bptr;

  b64 = BIO_new(BIO_f_base64());
  bmem = BIO_new(BIO_s_mem());
  b64 = BIO_push(b64, bmem);
  BIO_write(b64, input, length);
  BIO_flush(b64);
  BIO_get_mem_ptr(b64, &bptr);

  char *buff = (char *)malloc(bptr->length);
  memcpy(buff, bptr->data, bptr->length-1);
  buff[bptr->length-1] = 0;

  BIO_free_all(b64);

  return buff;
}

void printByteArray(unsigned char* x,int len)
{
	cout << "\n";
	for(int i = 0;i < len;i++)
		cout << (unsigned)(x[i]) << ",";
	cout << "\n";
}

unsigned char * decode(std::string x,int& outLen)
{
	//const byte* schars = ((const byte*)(x.c_str())); //Commented this on 12/08/09. Throws namespace errors otherwise. 
	char* pin = NULL;
	pin = (char*)x.c_str(); 
	int inLen = x.size();
	unsigned char* pout = (unsigned char*)malloc(sizeof(char) * (x.size() + 100));
	
	outLen = base64_decode(pin,pout,x.size() + 100);
	//cout << "\nAfter decoding : ";
	//printByteArray((unsigned char*)pout,outLen);
	return pout;

}

string signHeader(const string& toSign,const string& secKey)
{
	cout << "------------toSign--------" << toSign;
	string signedHeader;// = toSign;
	unsigned char* signatureByteForm = (unsigned char*)toSign.c_str();
	int n = toSign.size();

	int keyLen = 0;
	unsigned char* key;
	key = decode(secKey,keyLen);

	unsigned int md_len;
    unsigned char md[EVP_MAX_MD_SIZE];

	HMAC(evp_md, key, keyLen, signatureByteForm, n, md, &md_len);

	BIO* b64 = BIO_new(BIO_f_base64());
    BIO* bmem = BIO_new(BIO_s_mem());
    b64 = BIO_push(b64, bmem);
    BIO_write(b64, md, md_len);
    BIO_flush(b64);
    BUF_MEM *bptr;
    BIO_get_mem_ptr(b64, &bptr);

    signedHeader.resize(bptr->length - 1);
    memcpy(&signedHeader[0], bptr->data, bptr->length-1);

    BIO_free_all(b64);
	
	return signedHeader;
}

int createContainer(const string& baseUrl,const string& accountName,const string& containerName,const string& secKey)
{
	KEYVALUELIST headersToBeFormed;
	headersToBeFormed.push_back(std::make_pair("x-ms-date",get_date()));
	std::stringstream authHeader;
	
	/*// Now sign the request
            // For a blob, you need to use this Canonical form:
            //  VERB + "\n" +
            //  Content - MD5 + "\n" +
            //  Content - Type + "\n" +
            //  Date + "\n" +
            //  CanonicalizedHeaders +
            //  CanonicalizedResource;

            StringBuilder signature = new StringBuilder();

            // Verb
            signature.Append("PUT\n");

            // Content-MD5 Header
            signature.Append("\n");

            // Content-Type Header
            signature.Append("\n");

            // Then Date, if we have already added the x-ms-date header, leave this null
            signature.Append("\n");

            // Now for CanonicalizedHeaders
            // Add the datetime
            signature.Append("x-ms-date:" + request.Headers["x-ms-date"] + "\n");

            // Now for CanonicalizedResource
            // Format is /{0}/{1} where 0 is name of the account and 1 is resources URI path
            signature.Append("/589project/abcdcont");

            // Next, we need to encode our signature using the HMAC-SHA256 algorithm
            byte[] signatureByteForm = System.Text.Encoding.UTF8.GetBytes(signature.ToString());

            System.Security.Cryptography.HMACSHA256 hasher = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(localDevStoreKey));

            // Now build the Authorization header
            String authHeader = String.Format(CultureInfo.InvariantCulture,
                                       "{0} {1}:{2}", 
                                       "SharedKey",
                                       localDevStoreAccount,
                                       System.Convert.ToBase64String(hasher.ComputeHash(signatureByteForm)
                                       ));

	*/
	authHeader << "PUT\n";//VERB
	authHeader << "\n";//MD5 (Not Used)
	authHeader << "\n";//Content Type
	authHeader << "\n";//Date optional if using xms-date
	//get the date header
	for(KEYVALUELIST::const_iterator it = headersToBeFormed.begin();it != headersToBeFormed.end();it++){
		if(it->first == "x-ms-date"){
			authHeader << "x-ms-date:" << it->second << "\n";//date header
			break;
		}
	}	
	//authHeader << headersToBeFormed["x-ms-date"] << "\n";//date header
	authHeader << "/" << accountName << "/" << containerName;//Canonicalized Resource
	string signedString = "SharedKey ";
	signedString = signedString.append(accountName);
	signedString = signedString.append(":");
	signedString = signedString.append(signHeader(authHeader.str(),secKey));
	headersToBeFormed.push_back(std::make_pair("Authorization",signedString));


	//build headers
	CURLHEADER contHeaders = buildHeaders(headersToBeFormed);
	//cout << "***********Header sent*******************" <<  contHeaders;
	//set the curl options and invoke curl request
	curl_easy_setopt(curl, CURLOPT_UPLOAD, true);//HTTP PUT operation
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, contHeaders);
	curl_easy_setopt(curl,CURLOPT_URL,(baseUrl + "/" + containerName).c_str()); //perform
	curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0); // Content-Length: 0
	res = curl_easy_perform(curl);
	return 1;
	//aders.Add("x-ms-date", DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture));
}

int listContainers(const string& baseUrl,const string& accountName,const string& secKey)
{
	KEYVALUELIST headersToBeFormed;
	headersToBeFormed.push_back(std::make_pair("x-ms-date",get_date()));
	std::stringstream authHeader;
	
	authHeader << "GET\n";//VERB
	authHeader << "\n";//MD5 (Not Used)
	authHeader << "\n";//Content Type
	authHeader << "\n";//Date optional if using xms-date
	//get the date header
	for(KEYVALUELIST::const_iterator it = headersToBeFormed.begin();it != headersToBeFormed.end();it++){
		if(it->first == "x-ms-date"){
			authHeader << "x-ms-date:" << it->second << "\n";//date header
			break;
		}
	}	
	//authHeader << headersToBeFormed["x-ms-date"] << "\n";//date header
	authHeader << "/" << accountName << "/?comp=list"; // << containerName;//Canonicalized Resource
	
	string signedString = "SharedKey ";
	signedString = signedString.append(accountName);
	//signedString=signedString.append("/?comp=list");
	signedString = signedString.append(":");
	signedString = signedString.append(signHeader(authHeader.str(),secKey));
	headersToBeFormed.push_back(std::make_pair("Authorization",signedString));

	//build headers
	CURLHEADER contHeaders = buildHeaders(headersToBeFormed);
	//cout << "***********Header sent*******************" <<  contHeaders;
	//set the curl options and invoke curl request
	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST,"GET");//HTTP GET operation
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, contHeaders);
	curl_easy_setopt(curl,CURLOPT_URL,(baseUrl+ "/"+"?comp=list").c_str()); //perform
	//curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0); // Content-Length: 0
	res = curl_easy_perform(curl);
	return 1;
	//aders.Add("x-ms-date", DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture));
}

int deleteContainer(const string& baseUrl,const string& accountName,const string& containerName,const string& secKey)
{
	KEYVALUELIST headersToBeFormed;
	headersToBeFormed.push_back(std::make_pair("x-ms-date",get_date()));
	std::stringstream authHeader;
	
	authHeader << "DELETE\n";//VERB
	authHeader << "\n";//MD5 (Not Used)
	authHeader << "\n";//Content Type
	authHeader << "\n";//Date optional if using xms-date
	//get the date header
	for(KEYVALUELIST::const_iterator it = headersToBeFormed.begin();it != headersToBeFormed.end();it++){
		if(it->first == "x-ms-date"){
			authHeader << "x-ms-date:" << it->second << "\n";//date header
			break;
		}
	}	
	//authHeader << headersToBeFormed["x-ms-date"] << "\n";//date header
	authHeader << "/" << accountName << "/" << containerName;//Canonicalized Resource
	string signedString = "SharedKey ";
	signedString = signedString.append(accountName);
	signedString = signedString.append(":");
	signedString = signedString.append(signHeader(authHeader.str(),secKey));
	headersToBeFormed.push_back(std::make_pair("Authorization",signedString));


	//build headers
	CURLHEADER contHeaders = buildHeaders(headersToBeFormed);
	//cout << "***********Header sent*******************" <<  contHeaders;
	//set the curl options and invoke curl request
	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");//HTTP PUT operation
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, contHeaders);
	curl_easy_setopt(curl,CURLOPT_URL,(baseUrl + "/" + containerName).c_str()); //perform
	//curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0); // Content-Length: 0
	res = curl_easy_perform(curl);
	return 1;
	//aders.Add("x-ms-date", DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture));
}

int createBlob(const string& baseUrl,const string& accountName,const string& containerName,const string& blobName,const string& secKey)
{
	KEYVALUELIST headersToBeFormed;
	headersToBeFormed.push_back(std::make_pair("x-ms-date",get_date()));
	std::stringstream authHeader;
	
	authHeader << "PUT\n";//VERB
	authHeader << "\n";//MD5 (Not Used)
	authHeader << "\n";//Content Type
	authHeader << "\n";//Date optional if using xms-date
	//get the date header
	for(KEYVALUELIST::const_iterator it = headersToBeFormed.begin();it != headersToBeFormed.end();it++){
		if(it->first == "x-ms-date"){
			authHeader << "x-ms-date:" << it->second << "\n";//date header
			break;
		}
	}	
	//authHeader << headersToBeFormed["x-ms-date"] << "\n";//date header
	authHeader << "/" << accountName << "/" << containerName << "/" << blobName;//Canonicalized Resource
	string signedString = "SharedKey ";
	signedString = signedString.append(accountName);
	signedString = signedString.append(":");
	signedString = signedString.append(signHeader(authHeader.str(),secKey));
	headersToBeFormed.push_back(std::make_pair("Authorization",signedString));


	//build headers
	CURLHEADER contHeaders = buildHeaders(headersToBeFormed);
	//cout << "***********Header sent*******************" <<  contHeaders;
	//set the curl options and invoke curl request
	curl_easy_setopt(curl, CURLOPT_UPLOAD, true);//HTTP PUT operation
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, contHeaders);
	curl_easy_setopt(curl,CURLOPT_URL,(baseUrl + "/" + containerName+ "/" + blobName).c_str()); //perform
	curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0); // Content-Length: 0
	res = curl_easy_perform(curl);
	return 1;
	//aders.Add("x-ms-date", DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture));
}

int listBlobs(const string& baseUrl,const string& accountName,const string& contName,const string& secKey)

{
	KEYVALUELIST headersToBeFormed;
	headersToBeFormed.push_back(std::make_pair("x-ms-date",get_date()));
	std::stringstream authHeader;
	
	authHeader << "GET\n";//VERB
	authHeader << "\n";//MD5 (Not Used)
	authHeader << "\n";//Content Type
	authHeader << "\n";//Date optional if using xms-date
	//get the date header
	for(KEYVALUELIST::const_iterator it = headersToBeFormed.begin();it != headersToBeFormed.end();it++){
		if(it->first == "x-ms-date"){
			authHeader << "x-ms-date:" << it->second << "\n";//date header
			break;
		}
	}	
	//authHeader << headersToBeFormed["x-ms-date"] << "\n";//date header
	authHeader << "/" << accountName << "/" << contName << "?comp=list"; // << containerName;//Canonicalized Resource
	
	string signedString = "SharedKey ";
	signedString = signedString.append(accountName);
	//signedString=signedString.append("/?comp=list");
	signedString = signedString.append(":");
	signedString = signedString.append(signHeader(authHeader.str(),secKey));
	headersToBeFormed.push_back(std::make_pair("Authorization",signedString));

	//build headers
	CURLHEADER contHeaders = buildHeaders(headersToBeFormed);
	//cout << "***********Header sent*******************" <<  contHeaders;
	//set the curl options and invoke curl request
	curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST,"GET");//HTTP GET operation
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, contHeaders);
	curl_easy_setopt(curl,CURLOPT_URL,(baseUrl+ "/"+ contName + "?comp=list").c_str()); //perform
	//curl_easy_setopt(curl, CURLOPT_INFILESIZE, 0); // Content-Length: 0
	res = curl_easy_perform(curl);
	return 1;
	//aders.Add("x-ms-date", DateTime.UtcNow.ToString("R", CultureInfo.InvariantCulture));
}


std::wstring str2;

unsigned char* getUTFBytes(std::string x,int& len)
{
	unsigned char* a = (unsigned char*)x.c_str();
	int bytelength = x.size();
	str2 = std::wstring(x.length(), L' '); // Make room for characters
	// Copy string to wstring.
	std::copy(x.begin(), x.end(), str2.begin());
	//...
	unsigned char* unidata = a;//(const byte*)str2.data();
	len = bytelength;//str2.size();
	cout << "string : " << x;
	cout << "\nUTF bytes : ";
	for(unsigned int i = 0;i < bytelength;i++) cout << (unsigned)unidata[i] << ",";
	return (unsigned char*)unidata;
}



void getHMAC(unsigned char data[],int datalen,unsigned char key[],int keylen)
{
	cout << "\nBefore encryption : ";
	cout << "\nData : ";
	printByteArray(data,datalen);
	cout << "\nKey : ";
	printByteArray(key,keylen);

	unsigned char md[EVP_MAX_MD_SIZE];
	unsigned int mdlen;


	HMAC(evp_md, key, keylen, (unsigned char*)data, datalen, md, &mdlen);
	cout << "\nAfter encryption : ";
	cout << "\nData : ";
	printByteArray((unsigned char*)md,mdlen);

}




int main(int argc,char* argv[])
{
	if(argc > 1){
		cout << "Usage: ./MyApplication \n";
		return 0;
	}
	
	    
	curl = curl_easy_init();
	
	string account = "589project";
	string seckey = "OZdaDECfWigyx9c202Xb/O/XRSGr5n08WfoPhSr7Jpt/D+74YymTcCpVfNYMxyHe7k/ar7xmHuRlXZBFNa4wFQ==";
	string baseURL = "http://589project.blob.core.windows.net";
	
	int userChoice;

	while(true)
	{
		cout << "\n***********************************************\n";
		cout << "Please select one of the following options: \n";
		cout << "  1. Create a new container \n";
		cout << "  2. List existing containers \n";
		cout << "  3. Delete an existing container \n";
		cout << "  4. Create a new blob \n";
		cout << "  5. List existing blobs in a conatiner \n";
		cout << "  6. Exit \n";
		cout << "***********************************************\n\n";

		cin >> userChoice;
		cin.ignore(80,'\n');
	
	switch(userChoice)
	 {
		
		case 1: 
		{
			string newContainer;
			cout << "Enter the name of the container you wish to create \n";
			cin>> newContainer; 
			createContainer(baseURL,account,newContainer,seckey);
			break;
		}
		
		case 2: 
		{
			//string listContainerName;
			//cout << "Enter the name of the container you wish to create \n";
			//cin >> listContainerName; 
			listContainers(baseURL,account,seckey);
			break;
		}
		
		case 3: 
		{
			string delContainer;
			cout << "Enter the name of an existing container you wish to delete \n";
			cin >> delContainer;
			deleteContainer(baseURL,account,delContainer,seckey);
			break;
		}
	 	
		case 4: 
		{
			string blobContName,blobName;
			cout << "Enter the name of an existing container you wish to put your blob into: \n";
			cin >> blobContName;
			cout << "Enter the name of the blob you wish to create: \n";
			cin >> blobName;
			createBlob(baseURL,account,blobContName,blobName,seckey);
			break;
		}
		
	
		case 5: 
		{
			string blobsInContList;
			cout << "Enter the name of an existing container whose blobs are to be listed: \n";
			cin >> blobsInContList;
			listBlobs(baseURL,account,blobsInContList,seckey);
			break;
		}
		
		
		case 6: 
		{
			curl_easy_cleanup(curl);
			exit(1);
			break;
		}
				
		default:
		{
			cout << "Usage: <1-6> \n";
			break;
		}
		
	 }
	
	}
}

